package service

import (
	"code.huawei.com/tdc-sdk/utils"
	"encoding/json"
	"github.com/gomodule/redigo/redis"
	"github.com/pkg/errors"
	"github.com/spf13/cast"
	"nft-server/common"
	"nft-server/config"
	"nft-server/dao"
	"nft-server/db"
	"nft-server/logger"
	"nft-server/model"
	"nft-server/payment"
	"nft-server/util"
	"reflect"
	"time"
)

const (
	RedisOrderId = "#redisOrderId"

	DeFaultCurrency = "CNY"
	DeFaultPrice    = 100
)

//OrderStatusCanClose 可以关闭订单的订单状态
var OrderStatusCanClose = []string{util.TradeNoPay, util.TradeRevoked, util.TradePayError}

func GetOrderWithId(orderId string) (*model.Order, error) {
	order, err := dao.GetOrderDao().GetOrderByOrderId(orderId)
	if err != nil {
		return order, err
	}
	return order, nil
}
func GetOrderWithThirdId(thirdPartyId string) (*model.Order, error) {
	order, err := dao.GetOrderDao().GetOrderByThirdId(thirdPartyId)
	if err != nil {
		return order, err
	}
	return order, nil
}
func CreateOrder(userId int, collectionIndex string, subCollectionIndex int, tokenId string, collection *model.CollectionDetail) *model.Order {
	order := &model.Order{
		OrderId:            util.GetUUID32(),
		ThirdPartyId:       "",
		UserId:             userId,
		CollectionIndex:    collectionIndex,
		SubCollectionIndex: subCollectionIndex,
		TokenId:            tokenId,
		OrderStatus:        util.TradeNoPay,
		CreatedTime:        time.Now(),
		ExpireTime:         time.Now().Add(util.OrderLiftTime),
		PaymentTime:        util.GetDefaultTime(),
		Currency:           collection.Currency,
		Price:              collection.Price,
	}
	return order
}

func SaveOrder(order *model.Order) error {
	err := dao.GetOrderDao().SaveOrderInfo(order)
	if err != nil {
		return errors.Errorf("CreateOrder error: %v", err)
	}
	return nil
}

//UpdateOrderThirdPartyId 更新订单第三方平台的订单号
func UpdateOrderThirdPartyId(orderId, thirdPartyId string) error {
	oldOrder, err := dao.GetOrderDao().GetOrderByOrderId(orderId)
	if err != nil {
		return errors.Errorf("CloseOrder error: %v", err)
	}
	lock.Lock()
	oldOrder.ThirdPartyId = thirdPartyId
	err = dao.GetOrderDao().UpdateOrderInfo(oldOrder)
	lock.Unlock()
	if err != nil {
		return errors.Errorf("CloseOrder error: %v", err)
	}
	return nil
}

// UpdateOrderStatus 更新订单状态
func UpdateOrderStatus(oldOrder *model.Order, orderStatus string) error {
	lock.Lock()
	oldOrder.OrderStatus = orderStatus
	err := dao.GetOrderDao().UpdateOrderInfo(oldOrder)
	lock.Unlock()
	if err != nil {
		return errors.Errorf("CloseOrder error: %v", err)
	}
	return nil
}
func UpdateOrder(order *model.Order) error {
	lock.Lock()
	err := dao.GetOrderDao().UpdateOrderInfo(order)
	lock.Unlock()
	if err != nil {
		return errors.Errorf("CloseOrder error: %v", err)
	}
	return nil
}

func WriteOrderIntoRedis(order *model.Order, redisPool *redis.Pool) error {
	if redisPool == nil {
		return errors.Errorf("无法连接redis")
	}
	//将该订单信息手上写入redis
	conn := redisPool.Get()
	defer conn.Close()
	//该订单对应的key
	keyOfOrder := BuildOrderKeyForRedis(order.UserId)

	valueOfOrder, err := BuildOrderValueForRedis(order)
	if err != nil {
		logger.Error("can't write into redis error: %v", err)
		return err
	}

	_, err = conn.Do("SET", keyOfOrder, valueOfOrder)
	if err != nil {
		logger.Error("can't write into redis error: %v", err)
		return err
	}
	return nil
}
func GetOrderFromRedis(userId int, redisPool *redis.Pool) (order *model.Order, err error) {
	if redisPool == nil {
		return nil, errors.Errorf("can't connect redis")
	}
	conn := redisPool.Get()
	defer conn.Close()
	//该订单对应的key
	keyOfOrder := BuildOrderKeyForRedis(userId)
	valueOfOrder, err := conn.Do("GET", keyOfOrder)
	if err != nil {
		logger.Error(err.Error())
		return nil, errors.Errorf("can't found in redis")
	}
	if err == nil && valueOfOrder == nil {
		return nil, errors.Errorf("订单已完成或已关闭")
	}
	order, err = GetOrderFromRedisValue(cast.ToString(valueOfOrder))
	if err != nil {
		logger.Error(err.Error())
		return nil, errors.Errorf("订单解析失败")
	}
	return order, nil
}
func DeleteOrderFromRedis(userId int, redisPool *redis.Pool) (order *model.Order, err error) {
	if redisPool == nil {
		return nil, errors.Errorf("can't connect redis")
	}
	conn := redisPool.Get()
	defer conn.Close()
	//该订单对应的key
	keyOfOrder := BuildOrderKeyForRedis(userId)
	valueOfOrder, err := conn.Do("GET", keyOfOrder)
	if err != nil {
		return nil, err
	}
	if err == nil && valueOfOrder == nil {
		return nil, nil
	}
	_, err = conn.Do("DEL", keyOfOrder)
	if err != nil {
		logger.Error("can't delete value from redis error: %v", err)
		return nil, err
	}
	order, err = GetOrderFromRedisValue(cast.ToString(valueOfOrder))
	if err != nil {
		return nil, err
	}
	return order, nil
}
func BuildValueForRedisWithReflect(order model.Order) string {
	//通过反射建立redis存储条件
	var partString []string
	typeInfo := reflect.TypeOf(order)
	valueInfo := reflect.ValueOf(order)
	valueNum := valueInfo.NumField()
	for index := 0; index < valueNum; index++ {
		if typeInfo.Field(index).Type == reflect.TypeOf(time.Time{}) {
			value := cast.ToTime(valueInfo.Field(index).Interface())
			partString = append(partString, cast.ToString(util.GetMilliTime(value)))
		} else {
			value := valueInfo.Field(index).Interface()
			partString = append(partString, cast.ToString(value))
		}
	}
	valueOfOrder := util.CombineStringWithConnect(partString)
	return valueOfOrder
}
func BuildOrderKeyForRedis(userId int) string {
	//该订单对应的key
	keyOfOrder := util.CombineTwoString(cast.ToString(userId), RedisOrderId)
	return keyOfOrder
}
func BuildOrderValueForRedis(order *model.Order) (string, error) {
	//通过序列化建立redis存储条件
	data, err := json.Marshal(order)
	if err != nil {
		return "", err
	}
	return cast.ToString(data), nil
}
func GetOrderFromRedisValue(valueOfOrder string) (*model.Order, error) {
	order := new(model.Order)
	err := json.Unmarshal([]byte(valueOfOrder), order)
	if err != nil {
		return nil, err
	}
	return order, nil
}

//根据订单状态变化处理订单
func DealWithOrder(orderId string, lastTradeState string, lastTradeTime *time.Time) (txId string, err error) {
	order, err := GetOrderWithId(orderId)
	if err != nil {
		logger.Error("GetOrderWithId error: %v", err)
		return "", err
	}
	if lastTradeState == util.TradeNoPay {
		//用户未支付
		//超时判断
		//超时关闭，否则直接返回
		return ProcessNoPayOrder(order, lastTradeState)
	} else if lastTradeState == util.TradeSuccess {
		//支付成功
		//更新订单状态
		if lastTradeTime != nil {
			order.PaymentTime = *lastTradeTime
		}
		return ProcessSuccessOrder(order, lastTradeState)
	} else if lastTradeState == util.TradePaying {
		//支付中
		//更新订单状态
		return ProcessPayingOrder(order, lastTradeState)
	} else if lastTradeState == util.TradeRevoked || lastTradeState == util.TradePayError || lastTradeState == util.TradeClosed {
		//已撤销,支付失败,已关闭
		//第三方关闭订单
		return ProcessFailOrder(order, lastTradeState)
	}
	//}
	return "", nil
}
func ProcessNoPayOrder(order *model.Order, lastTradeState string) (txId string, err error) {
	if order.ExpireTime.Before(time.Now()) {
		//第三方关闭订单
		err = payment.ExampleCloseOrder(order.OrderId)
		if err != nil {
			logger.Error("ExampleCloseOrder error: %v", err)
			return "", err
		}
		//从缓存中删除订单
		oldOrder, err := DeleteOrderFromRedis(order.UserId, db.RedisPool)
		if err != nil {
			logger.Error("DeleteOrderFromRedis error: %v", err)
			return "", err
		}
		if oldOrder == nil && err == nil {
			return "", nil
		}
		// 平台内修改订单状态为已取消
		err = UpdateOrderStatus(order, util.TradeClosed)
		if err != nil {
			logger.Error("UpdateOrderStatus error: %v", err)
			return "", err
		}
		//写回库存
		err = WriteOneTokenIntoRedis(order.CollectionIndex, order.SubCollectionIndex, order.TokenId, db.RedisPool)
		if err != nil {
			logger.Error("WriteOneTokenIntoRedis error: %v", err)
			return "", err
		}
	}
	return "", nil
}
func ProcessSuccessOrder(order *model.Order, lastTradeState string) (txId string, err error) {
	//从缓存中删除订单
	oldOrder, err := DeleteOrderFromRedis(order.UserId, db.RedisPool)
	if err != nil {
		logger.Error("DeleteOrderFromRedis error: %v", err)
		return "", err
	}
	if oldOrder == nil && err == nil {
		return "", nil
	}
	//以下改为消费者完成--todo
	//更新DB状态
	err = UpdateOrderStatus(order, util.TradeSuccess)
	if err != nil {
		logger.Error("UpdateOrderStatus error: %v", err)
		return "", err
	}
	//藏品转移
	txId, err = ApplyAssets(order.CollectionIndex, order.SubCollectionIndex, order.TokenId, order.UserId)
	if err != nil {
		logger.Error("ApplyAssets failed: %v", err)
		return "", err
	}
	return txId, nil
}
func ProcessPayingOrder(order *model.Order, lastTradeState string) (txId string, err error) {
	err = UpdateOrderStatus(order, util.TradePaying)
	if err != nil {
		logger.Error("UpdateOrderStatus error: %v", err)
		return "", err
	}
	return "", nil
}
func ProcessFailOrder(order *model.Order, lastTradeState string) (txId string, err error) {
	err = payment.ExampleCloseOrder(order.OrderId)
	if err != nil {
		logger.Error("ExampleCloseOrder error: %v", err)
		return "", err
	}
	//从缓存中删除订单
	oldOrder, err := DeleteOrderFromRedis(order.UserId, db.RedisPool)
	if err != nil {
		logger.Error("DeleteOrderFromRedis error: %v", err)
		return "", err
	}
	if oldOrder == nil && err == nil {
		return "", nil
	}
	//更新订单状态
	err = UpdateOrderStatus(order, lastTradeState)
	if err != nil {
		logger.Error("UpdateOrderStatus error: %v", err)
		return "", err
	}
	//释放库存
	err = WriteOneTokenIntoRedis(order.CollectionIndex, order.SubCollectionIndex, order.TokenId, db.RedisPool)
	if err != nil {
		logger.Error("WriteOneTokenIntoRedis error: %v", err)
		return "", err
	}
	return "", nil
}

func GetOrderWithMultiUser(userIds []int, page int, pageSize int) (orders []*model.Order, size int, totalNum int, err error) {
	return dao.GetOrderDao().GetOrderWithMultiUserId(userIds, page, pageSize)
}
func CheckOverTimeOrder(userId string, nowTime time.Time) error {
	orders, err := dao.GetOrderDao().GetAndUpdateOverTimeOrder(userId, nowTime)
	if err != nil {
		return err
	}
	for _, order := range orders {
		if order.ExpireTime.Before(nowTime) {
			//第三方关闭订单
			err = payment.ExampleCloseOrder(order.OrderId)
			if err != nil {
				logger.Error("ExampleCloseOrder error: %v", err)
				return err
			}
			//从缓存中删除订单
			_, err = DeleteOrderFromRedis(order.UserId, db.RedisPool)
			if err != nil {
				logger.Error("DeleteOrderFromRedis error: %v", err)
				return err
			}
			//写回库存
			err = WriteOneTokenIntoRedis(order.CollectionIndex, order.SubCollectionIndex, order.TokenId, db.RedisPool)
			if err != nil {
				logger.Error("WriteOneTokenIntoRedis error: %v", err)
				return err
			}
		}
	}
	return nil
}
func GetAllOrderWithPage(query *common.OrderQueryReq, nowTime time.Time) (orders []*model.Order, size int, totalNum int, err error) {
	if len(query.CollectionIndex) < 1 {
		return nil, 0, 0, nil
	}
	return dao.GetOrderDao().GetAllOrderWithPage(query, nowTime)
}

func ConstructOrderInfo(userName string, response *common.OrderInfoForQuery, orders []*model.Order) error {
	dacConfig, err := utils.NewConfig(config.GetConfig().DACConfig.ConfigPath)
	if err != nil {
		return err
	}
	for _, eachOrder := range orders {
		metaData, err := GetMetaDataWithCollectionIndexAndTokenIdWithoutUserId(eachOrder.CollectionIndex, eachOrder.TokenId)
		detail := MetaDataDetail{}
		if err = json.Unmarshal([]byte(metaData.TokenURI), &detail); err != nil {
			return err
		}
		asset, err := GetAsset(eachOrder.CollectionIndex, eachOrder.TokenId)
		assetUrl, err := util.GetAssetUrl(detail.AssetUrl)
		collection, err := GetCollectionDetailById(eachOrder.CollectionIndex, eachOrder.SubCollectionIndex)
		if err != nil {
			return err
		}
		assetId := asset.AssetID
		if dacConfig.Service.Type != SharedMode {
			//专享版
			assetId = GetAssetIdInPrivate(eachOrder.CollectionIndex, eachOrder.TokenId)
		}
		oneOrder := common.OrderInfo{
			UserId:             cast.ToString(eachOrder.UserId),
			UserName:           userName,
			OrderId:            eachOrder.OrderId,
			ThirdPartyId:       eachOrder.ThirdPartyId,
			CollectionIndex:    eachOrder.CollectionIndex,
			SubCollectionIndex: eachOrder.SubCollectionIndex,
			AssetUrl:           assetUrl,
			CollectionName:     collection.CollectionName,
			PictureName:        detail.Name,
			Issuer:             collection.Issuer,
			TokenId:            eachOrder.TokenId,
			AssetId:            assetId,
			OrderStatus:        eachOrder.OrderStatus,
			CreatedTime:        util.GetMilliTime(eachOrder.CreatedTime),
			ExpireTime:         util.GetMilliTime(eachOrder.ExpireTime),
			PaymentTime:        util.GetMilliTime(eachOrder.PaymentTime),
			Price:              eachOrder.Price,
		}
		response.Order = append(response.Order, oneOrder)
	}
	return nil
}
